package week8;

/**
 * Bord voor het Boter-Kaas-Eieren spel.
 * Practicumopdracht P1.
 * @author  Theo Ruys en Arend Rensink
 * @version $Revision: 1.4 $
 */
public class Bord {

    // -- Constants --------------------------------------------------

    public  static final int      DIM       = 3 ;
	private static final char[][] NUMMERINGCHARS = {
		{' ', '0', ' ', (char)186, ' ', '1', ' ', (char)186, ' ', '2', ' ' },
		{(char)205, (char)205, (char)205, (char)206, (char)205, (char)205, (char)205, (char)206, (char)205, (char)205, (char)205 },
		{' ', '3', ' ', (char)186, ' ', '4', ' ', (char)186, ' ', '5', ' ' },
		{(char)205, (char)205, (char)205, (char)206, (char)205, (char)205, (char)205, (char)206, (char)205, (char)205, (char)205 },
		{' ', '6', ' ', (char)186, ' ', '7', ' ', (char)186, ' ', '8', ' ' }
	};
    private static final String[] NUMMERING = { new String(NUMMERINGCHARS[0]),
                                                new String(NUMMERINGCHARS[1]),
                                                new String(NUMMERINGCHARS[2]),
                                                new String(NUMMERINGCHARS[3]),
                                                new String(NUMMERINGCHARS[4]) } ; 
    private static final String   LINE      = NUMMERING[1];
    private static final String   DELIM     = "     " ;
	private static final char 	  SEPARATOR = (char)186;

    // -- Instance variables -----------------------------------------

    /**
     * De DIM*DIM vakjes die het Boter-Kaas-Eieren bord vormen.
     * Zie NUMMERING voor de codering van de vakjes.
     * @invariant vakjes.length = DIM*DIM
     *            for 0<=i<vakjes.length: 
     *              vakjes[i] == Mark.LEEG | Mark.XX | Mark.OO
     */
    private Mark[] vakjes;

    // -- Constructors -----------------------------------------------

    /**
     * Creeert een leeg bord.
     * @ensure for <code>0<=i<DIM*DIM: this.getVakje(i) == Mark.LEEG</code>
     */
    public Bord() {
		vakjes = new Mark[DIM * DIM];
		reset();
    }

    // -- Queries ----------------------------------------------------

    /**
     * Creeert een deepcopy van dit bord.
     * @ensure <code>result != this</code><br>
     *         for <code>0 <= i < DIM*DIM:
     *             result.getVakje(i) == this.getVakje(i)</code>
     */
    public Bord deepCopy() {
		Bord kopie = new Bord();
		kopie.vakjes = new Mark[DIM*DIM];
		for(int i = 0; i < vakjes.length; i++){
			setVakje(i, getVakje(i));
		}
		return kopie;
    }

    /**
     * Berekent de index in de lineaire array van vakjes van 
     * een (rij,kol)-paar.
     * @require <code>0 <= i,j < DIM</code>
     * @return  de index behorende bij het (rij,kol)-vakje
     */
    public int index(int rij, int kol) {
		return DIM * rij + kol;
    }

    /**
     * Levert true op als de <code>ix</code> een geldige index 
     * is van een vakje op dit speelbord.
     * @ensure  <code>result == 0 <= ix < DIM*DIM</code>
     * @return  <code>true</code> als <code>0 <= ix < DIM*DIM</code>
     */
    public boolean isVakje(int ix) {
		return 0 <= ix && ix < DIM*DIM;
    }

    /**
     * Levert true op als de (rij,kol)-addressering geldig is voor
     * een vakje op het speelbord.
     * @ensure  <code>result == 0 <= rij,kol < DIM</code>
     * @return  true als <code>0 <= rij < DIM && 0 <= kol < DIM</code>
     */
    public boolean isVakje(int rij, int kol) {
		int index = index(rij, kol);
		return isVakje(index);
    }

    /**
     * Levert de inhoud van vakje <code>i</code>.
     * @require <code>this.isVakje(i)</code>
     * @ensure  <code>result == Mark.LEEG | Mark.XX | Mark.OO</code>
     * @param   i het nummer van het vakje (zie NUMMERING)
     * @return  de markering van het i-de vakje
     */
    public Mark getVakje(int i) {
		return vakjes[i];
    }

    /**
     * Levert de inhoud van vakje van rij <code>rij</code> en 
     * kolom <code>kol</code>.
     * @require <code>this.isVakje(rij,kol)</code>
     * @ensure  <code>result == Mark.LEEG | Mark.XX | Mark.OO</code>
     * @param   rij de rij van het vakje
     * @param   kol de kolom van het vakje
     * @return  de markering van het (rij,kol)-de vakje
     */
    public Mark getVakje(int rij, int kol) {
		int index = index(rij, kol);
		return getVakje(index);
    }

    /**
     * Levert true op als het vakje <code>i</code> leeg is.
     * @require <code>this.isVakje(i)</code>
     * @ensure  <code>result == (this.getVakje(i) == LEEG)</code>
     * @param   i het nummer van het vakje (zie NUMMERING)
     * @return  true als het vakje leeg is.
     */
    public boolean isLeegVakje(int i) {
		return getVakje(i).equals(Mark.LEEG);
	}

    /**
     * Levert true op als het vakje van rij <code>rij</code> en 
     * kolom <code>kol</code> leeg is.
     * @require <code>this.isVakje(rij,kol)</code>
     * @ensure  <code>result == Mark.LEEG | Mark.XX | Mark.OO</code>
     * @param   rij de rij van het vakje
     * @param   kol de kolom van het vakje
     * @return  true als het (rij,kol)-de vakje leeg is.
     */
    public boolean isLeegVakje(int rij, int kol) {
		int index = index(rij, kol);
		return isLeegVakje(index);
    }

    /**
     * Test of het gehele bord vol is.
     * @ensure <code>result == for all 0 <= i < DIM*DIM: 
     *                                 this.getVakje(i) != LEEG</code>
     * @return true als alle vakjes bezet zijn.
     */
    public boolean isVol() {
		boolean result = true;
		for(int i = 0; i < vakjes.length; i++){
			result &= !isLeegVakje(i);
		}
		return result;
    }

    /**
     * Levert true op als het spel afgelopen is. <br>
     * Het is spel is afgelopen als er een winnaar is of als 
     * het gehele bord vol is.
     * @ensure result == this.isVol() || this.heeftWinnaar()
     * @return true als het spel afgelopen is.
     */
    public boolean gameOver() {
		return isVol() || heeftWinnaar();
    }

    /**
     * Controleert of er een <b>rij</b> is die geheel uit 
     * <code>m</code>'s bestaat.
     * @require <code>m == Mark.LEEG | Mark.XX | Mark.OO</code>
     * @ensure  <code>result == true</code> als er een rij is die 
     *          alleen uit <code>m</code>'s bestaat.
     * @param   m de markering die gecontroleerd wordt
     * @return  true als er een rij is met alleen maar <code>m</code>'s
     */
    public boolean heeftRij(Mark m) {
		boolean result = false;
		for(int i = 0; i < DIM; i++){
			int count = 0;
			for(int j = 0; j < DIM; j++){
				if(getVakje(j, i).equals(m))
					count++;
			}
			result |= count == 3;
		}
		return result;
    }

    /**
     * Controleert of er een <b>kolom</b> is die geheel uit 
     * <code>m</code>'s bestaat.
     * @require <code>m == Mark.LEEG | Mark.XX | Mark.OO</code>
     * @ensure  <code>result == true</code> als er een kolom is die 
     *          alleen uit <code>m</code>'s bestaat.
     * @param   m de markering die gecontroleerd wordt
     * @return  true als er een kolom is met alleen maar <code>m</code>'s
     */
    public boolean heeftKolom(Mark m) {
		boolean result = false;
		for(int i = 0; i < DIM; i++){
			int count = 0;
			for(int j = 0; j < DIM; j++){
				if(getVakje(i, j).equals(m))
					count++;
			}
			result |= count == DIM;
		}
		return result;
    }

    /**
     * Controleert of er <b>diagonaal</b> is die geheel uit
     * <code>m</code>'s bestaat.
     * @require <code>m == Mark.LEEG | Mark.XX | Mark.OO</code>
     * @ensure  <code>result == true</code> als er een diagonaal is die 
     *          alleen uit <code>m</code>'s bestaat.
     * @param   m de markering die gecontroleerd wordt
     * @return  true als er een diagonaal is met alleen maar <code>m</code>'s
     */
    public boolean heeftDiagonaal(Mark m) {
		boolean result = false;
		int count = 0;
		int revCount = 0;
		for(int i = 0; i < DIM; i++){
			if(getVakje(i, i).equals(m))
				count++;
			if(getVakje(i, DIM - i - 1).equals(m))
				revCount++;
		}
		result |= count == DIM;
    	result |= revCount == DIM;
		
		return result;
	}

    /**
     * Controleert of de markering <code>m</code> gewonnen heeft. <br>
     * Een markering <code>m</code> heeft gewonnen dit Bord minstens
     * een rij, een kolom, of diagonaal heeft die alleen maar uit
     * <code>m</code>'s bestaat.
     * @require <code>m == Mark.XX | Mark.OO</code>
     * @ensure  <code>result == this.heeftRij(m) || 
     *                          this.heeftKolom(m) ||
     *                          this.heeftDiagonaal(m)</code>
     * @param   m de markering die gecontroleerd gaat worden
     * @return  true als de markering <code>m</code> gewonnen heeft
     */
    public boolean isWinnaar(Mark m) {
		boolean result = false;
			result |= heeftRij(m);
			result |= heeftKolom(m);
			result |= heeftDiagonaal(m);
		return result;
    }

    /**
     * Levert true op als het spel een winnaar heeft. <br>
     * Er is een winnaar als er een markering is die minstens
     * een rij, kolom of diagonaal in zijn bezit heeft.
     * @ensure <code>result == isWinnaar(XX) || isWinnaar(OO)</code>
     * @return true als dit Bord een winnaar heeft.
     */
    public boolean heeftWinnaar() {
		boolean result = false;
			result |= isWinnaar(Mark.XX);
			result |= isWinnaar(Mark.OO);
		return result;
    }

    /**
     * Levert een String-representatie van dit Bord op. <br>
     * Naast de huidige toestand van het Bord, schrijft de methode
     * ook de <code>NUMMERING</code> van de vakjes naast het Bord.
     * @return de toestand van dit Bord als String.
     */
    public String toString() {
        String s = "";
        for (int i=0; i<DIM; i++) {
            String rij = "";
            for (int j=0; j<DIM; j++) {
                rij = rij + " " + getVakje(i,j).toString() + " " ;
                if (j<DIM-1) rij = rij + SEPARATOR ;
            }
            s = s + rij + DELIM + NUMMERING[i*2] ;
            if (i<DIM-1)
                s = s + "\n" + LINE + DELIM + NUMMERING[i*2+1] + "\n";
        }
        return s;
    }

    // -- Commands ---------------------------------------------------

    /**
     * Maakt alle vakjes van dit bord leeg (d.w.z. LEEG).
     * @ensure for <code>0 <= i < DIM*DIM: this.getVakje(i) == LEEG</code>
     */
    public void reset() {
	for(int i = 0; i < vakjes.length; i++){
			setVakje(i, Mark.LEEG);
		}
    }

    /**
     * Zet de inhoud van vakje <code>i</code> op markering <code>m</code>.
     * @require <code>this.isVakje(i)</code>
     *          <code>m == Mark.LEEG | Mark.XX | Mark.OO</code><br>
     * @ensure  <code>this.getVakje(i) == m</code>
     * @param   i het nummer van het vakje (zie NUMMERING)
     * @param   m de gewenste markering van het i-de vakje
     */
    public void setVakje(int i, Mark m) {
		vakjes[i] = m;
    }

    /**
     * Zet de inhoud van het vakje van rij <code>rij</code> en kolom
     * <code>kol</code> op markering <code>m</code>. <br>
     * @require <code>this.isVakje(rij,kol)</code>
     *          <code>m == Mark.LEEG | Mark.XX | Mark.OO</code><br>
     * @ensure  <code>this.getVakje(i) == m</code>
     * @param   rij de rij van het vakje
     * @param   kol de kolom van het vakje
     * @param   m de gewenste markering van het (rij,kol)-de vakje
     */
    public void setVakje(int rij, int kol, Mark m) {
		int index = index(rij, kol);
		setVakje(index, m);
    }

} // end of class Bord
